#include "i2c_oled.h"
#include "i2c_oled_font.h"

uint8_t oled_8x16_col_to_x(uint8_t col);
uint8_t oled_8x16_line_to_y(uint8_t row);
uint8_t oled_6x8_col_to_x(uint8_t col);
uint8_t oled_6x8_line_to_y(uint8_t row);

static uint8_t OLED_CHAR_SIZE = 16;	/* 字库宽8x高16 */
uint8_t (*oled_col_to_x)(uint8_t) = oled_8x16_col_to_x;
uint8_t (*oled_line_to_y)(uint8_t) = oled_8x16_line_to_y;
void _oled_show_error(uint8_t x, uint8_t y, uint8_t *err);

/**
 * @brief i2c write a byte to specific address
 * 
 * @param i2c_addr 
 * @param i2c_data 
 */
void i2c_write_byte(uint8_t i2c_addr, uint8_t i2c_data){
	while(i2c_flag_get(I2C0, I2C_FLAG_I2CBSY));
	i2c_start_on_bus(I2C0);
	while(!i2c_flag_get(I2C0, I2C_FLAG_SBSEND));    // start byte send
	i2c_master_addressing(I2C0, OLED_I2C_ADDR, I2C_TRANSMITTER);

	while(!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND));   // address send
	i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);        

	while(SET != i2c_flag_get(I2C0, I2C_FLAG_TBE)); // transmit byte empty?
	i2c_data_transmit(I2C0, i2c_addr);
	while(!i2c_flag_get(I2C0, I2C_FLAG_BTC));	//byte transmit complete
	i2c_data_transmit (I2C0, i2c_data);
	while(!i2c_flag_get(I2C0, I2C_FLAG_BTC));
	i2c_stop_on_bus (I2C0);
	while(I2C_CTL0(I2C0)&0x0200);
}

/**
 * Note: OLED的Page地址模式下，从高到低分为Page0-Page7 7个行，从左到右分为COL0-COL127 127个列
 * 列号高四位和低四位分别有专门的设置
 */
/**
 * @brief oled设置字体大小，有8x16和6x8两种方式（宽x高）
 * 
 * @param char_size 
 */
void oled_set_char_size(uint8_t char_size)
{
	switch(char_size){
		case 8:	
			OLED_CHAR_SIZE = 8;		/* 6x8 画布 */
			oled_col_to_x = oled_6x8_col_to_x;
			oled_line_to_y = oled_6x8_line_to_y; 
			break;
		case 16: 
			OLED_CHAR_SIZE = 16; 	/* 8x16 画布 */
			oled_col_to_x = oled_8x16_col_to_x;
			oled_line_to_y = oled_8x16_line_to_y; 
			break;
		default:
			OLED_CHAR_SIZE = 0;		/* can't not find correspond canvas */
			oled_col_to_x = NULL;
			oled_line_to_y = NULL; 
			_oled_show_error(0, 0, (uint8_t *)"wrong char_size setted.");
			break;
	}
}

/* set col in 8x16 canvas to x coodinrate in the page */
inline uint8_t oled_8x16_col_to_x(uint8_t col)
{
	return (col - 1) * 8; 
}

/* set line in 8x16 canvas to y coodinrate in the page */
inline uint8_t oled_8x16_line_to_y(uint8_t line)
{
	return (line - 1) * 2;
}

inline uint8_t oled_6x8_col_to_x(uint8_t col)
{
	return (col - 1) * 6;
}

inline uint8_t oled_6x8_line_to_y(uint8_t line)
{
	return (line - 1);
}



void oled_write_command(uint8_t oled_cmd){
	i2c_write_byte(OLED_I2C_CMD_ADDR, oled_cmd); 
}


void oled_write_data(uint8_t oled_data){
	i2c_write_byte(OLED_I2C_DATA_ADDR, oled_data); 
}


/**
* 功能：
* 输入：无
**/
void oled_init(void){
	oled_write_command(0xAE);// 0xAE:关显示，0xAF:开显示
	
	oled_write_command(0x00);// 设置开始地址的低字节       
	oled_write_command(0x10);// 设置开始地址的高字节
	
	oled_write_command(0xd5);// 命令头，设置显示时钟分频比/振荡器频率
	oled_write_command(0x80);// 设置分割比率，设置时钟为100帧/秒
	
	oled_write_command(0xa8);// 命令头，设置多路复用率(1 to 64)
	oled_write_command(0x3f);// --1/64 duty
	
	oled_write_command(0xd3);// 命令头，设置显示偏移移位映射RAM计数器(0x00~0x3F)
	oled_write_command(0x00);// 不偏移
	
	oled_write_command(0x00);// 写入页位置（0xB0~7）
	oled_write_command(0x40);// 显示开始线
	
	oled_write_command(0x8d);// VCC电源
	oled_write_command(0x14);// --set(0x10) disable
	
	oled_write_command(0xa1);// 设置段重新映射
	oled_write_command(0xc8);// 设置Y轴扫描方向，0xc0上下反置 ，0xc8正常（上边为0行）
	
	oled_write_command(0xda);// 命令头，--set com pins hardware configuration
	oled_write_command(0x12);
	
	oled_write_command(0x81);// 对比度，指令：0x81，数据：0~255（255最高）
	oled_write_command(0xff);
	
	oled_write_command(0xd9);// 命令头，--set pre-charge period
	oled_write_command(0xf1);// Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	
	oled_write_command(0xdb);// 命令头，--set vcomh
	oled_write_command(0x30);// Set VCOM Deselect Level
	
	oled_write_command(0x20);// 水平寻址设置
	oled_write_command(0x00);
	
	oled_write_command(0xa4);// 0xa4:正常显示，0xa5:整体点亮
	oled_write_command(0xa6);// 0xa6:正常显示，0xa7:反色显示
	
	oled_write_command(0xAF);// 0xAE:关显示，0xAF:开显示
}


// OLED显示单一色度
void oled_fill(uint8_t fill_data){
	uint8_t i=0, n;
	for(i = 0; i < 8; i++){
		oled_write_command (0xB0+i);    //设置页地址（0~7）
		oled_write_command (0x00);      //设置显示位置—列低地址
		oled_write_command (0x10);      //设置显示位置—列高地址  		
		for(n = 0; n < 132; n++){
			oled_write_data(fill_data);
		}
	}
}

// OLED 白屏显示
void oled_display_white(void){
	oled_fill(0xFF);
}

// OLED 清屏显示
void oled_clear_all(void){

	oled_fill(0x00);
}

// OLED打开
void oled_on(){
	oled_write_command(0xAF);  // 开启显示
	oled_write_command(0x8D);  // 设置电荷泵
	oled_write_command(0x14);  // 开启电荷泵
}

// OLED关闭
void oled_off(){
	oled_write_command(0xAF);  // 开启显示
	oled_write_command(0x8D);  // 设置电荷泵
	oled_write_command(0x10);  // 关闭电荷泵
}



// OLED设置显示位置
static void _oled_set_pos(uint8_t x, uint8_t y){
	oled_write_command(0xB0 + y);					//设置Y位置
	oled_write_command((x & 0xf0) >> 4 | 0x10 );		//设置X位置高4位
	oled_write_command((x & 0x0f) | 0x01 );				//设置X位置低4位
}


/*************************************************************************************************************************/
/*************************************          oled lower-level api                    **********************************/
/*************************************************************************************************************************/


static void _oled_show_chinese(uint8_t x, uint8_t y, uint8_t index){
	uint8_t i;
	_oled_set_pos(x, y);
	for(i = 0; i < 16; i++){
		oled_write_data(Hzk[2*index][i]);
	}
	_oled_set_pos(x, y+1);
	for(i = 0; i < 16; i++){
		oled_write_data(Hzk[2*index + 1][i]);
	}
}

/**
 * @brief oled low-level show char api
 * 
 * @param x x cood in page
 * @param y y cood in page
 * @param chr char
 * @param char_size select canvas for show char, char_size=16->8x16 canvas 8->6x8 canvas
 */
static void _oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint8_t char_size){
	uint8_t c_index = 0, i = 0;
	c_index = chr - ' ';  
	if(char_size == 16){
		/* 8x16 canvas */
		if(x > MAX_COLUMN - 8){ //if column space for show is not enough, jump to next column
			x = 0;
			y += 2;				
		}
		/* 8x16 upper half part show */
		_oled_set_pos(x, y);
		for(i = 0; i < 8; i++){
			oled_write_data(F8X16[c_index*16 + i]);
		}
		/* 8x16 lower half part show */
		_oled_set_pos(x, y+1);
		for(i = 0; i < 8; i++){
			oled_write_data(F8X16[c_index*16 + i + 8]);
		}
	}else if(char_size == 8){
		/* 6x8 canvas */
		if(x > MAX_COLUMN - 6){ //if column space for show is not enough, jump to next column
			x = 0;
			y += 1;
		}
		_oled_set_pos(x, y);
		for(i = 0; i < 6; i++){
			oled_write_data(F6x8[c_index][i]);
		}
	}else{
		_oled_show_error(x, y, (uint8_t *)"wrong char_size setted.");
	}
}

/**
 * @brief oled low-level show string api
 * 
 * @param x x cood in page
 * @param y y cood in page
 * @param str string
 * @param char_size select canvas for show char, char_size=16->8x16 canvas 8->6x8 canvas
 */
static void _oled_show_string(uint8_t x, uint8_t y, uint8_t *str, uint8_t char_size){
	uint8_t i = 0;
	while(str[i] != '\0'){
		_oled_show_char(x, y, str[i], char_size);
		if(char_size == 16){
			x += 8;
			if(x > MAX_COLUMN - 8){
				x = 0;
				y += 2;
			}
		}else if(char_size == 8){
			x += 6;
			if(x > MAX_COLUMN - 8){
				x = 0;
				y += 1;
			}
		}
		i++;
	}
}

// show oled error in cood(x,y)
void _oled_show_error(uint8_t x, uint8_t y, uint8_t *err){
	_oled_show_string(x, y, err, 16);
}


/*****************************************************************************************************************************************************/
/*********************************************  OLED external API    ********************************************************************************/
/*****************************************************************************************************************************************************/
void oled_show_chinese(uint8_t line, uint8_t column, uint8_t index)
{
	if(!oled_col_to_x || !oled_line_to_y){
		_oled_show_error(column, line, "wrong char size");
		return;
	}
	_oled_show_chinese(oled_col_to_x(column), oled_line_to_y(line), index);
}

void oled_show_error(uint8_t line, uint8_t column, uint8_t *err)
{
	if(!oled_col_to_x || !oled_line_to_y){
		_oled_show_error(column, line, "wrong char size");
		return;
	}
	_oled_show_error(oled_col_to_x(column), oled_line_to_y(line), err);
}

/**
  * @brief  OLED pow algo
  * @retval 
  */
uint32_t oled_pow(uint32_t x, uint32_t y)
{
	uint32_t res = 1;
	while(y--)
	{
		res *= x;
	}
	return res;
}

/**
 * @brief oled set show position(line and column) in canvas
 * 
 * @param line line start to show, range: 1-4(8x16)  1-8(6x8)
 * @param column column start to show, range: 1-16(8x16) 1-21(6x8)
 */
void oled_set_cursor(uint8_t line, uint8_t column)
{
	if(!oled_col_to_x || !oled_line_to_y){
		_oled_show_error(column, line, "wrong char size");
		return;
	}
	_oled_set_pos(oled_col_to_x(column), oled_line_to_y(line));	
}

/**
 * @brief 
 * 
 * @param line line start to show, range: 1-4(8x16)  1-8(6x8)
 * @param column column start to show, range: 1-16(8x16) 1-21(6x8)
 * @param ch char
 */
void oled_show_char(uint8_t line, uint8_t column, uint8_t ch)
{
	if(!oled_col_to_x || !oled_line_to_y){
		_oled_show_error(column, line, "wrong char size");
		return;
	}
	_oled_show_char(oled_col_to_x(column), oled_line_to_y(line), ch, OLED_CHAR_SIZE);
}

/**
 * @brief 
 * 
 * @param line line start to show, range: 1-4(8x16)  1-8(6x8)
 * @param column column start to show, range: 1-16(8x16) 1-21(6x8)
 * @param str string 
 */
void oled_show_string(uint8_t line, uint8_t column, char *str)
{
	if(!oled_col_to_x || !oled_line_to_y){
		_oled_show_error(column, line, "wrong char size");
		return;
	}
	_oled_show_string(oled_col_to_x(column), oled_line_to_y(line), str, OLED_CHAR_SIZE);
}

/**
  * @brief 
  * @param  line line start to show, range 1~4
  * @param  column column start to show, range 1~16
  * @param  num number for show, range 0~4294967295
  * @param  len length of number string, range 1~10
  * @retval 
  */
void oled_show_num(uint8_t line, uint8_t column, uint32_t num, uint8_t len)
{
	uint8_t i;
	for(i = 0; i < len; i++)
	{
		oled_show_char(line, column + i, num / oled_pow(10, len - i - 1) % 10 + '0');
	} 
}


/**
  * @brief  
  * @param  line line start to show, 1~4
  * @param  column column start to show, 1~16
  * @param  num number for show, range -2147483648~2147483647
  * @param  len length of number string, range 1~10
  * @retval 
  */
void oled_show_signed_num(uint8_t line, uint8_t column, int32_t num, uint8_t len)
{
	uint8_t i;
	uint32_t num1;
	if (num >= 0)
	{
		oled_show_char(line, column, '+');
		num1 = num;
	}
	else
	{
		oled_show_char(line, column, '-');
		num1 = -num;
	}
	for (i = 0; i < len; i++)
	{
		oled_show_char(line, column + i + 1, num1 / oled_pow(10, len - i - 1) % 10 + '0');
	}
}

/**
  * @brief  
  * @param  line line start to show, 1~4
  * @param  column column start to show, 1~16
  * @param  num number for show, range 0~0xFFFFFFFF
  * @param  len length of number string, range1~8
  * @retval 
  */
void oled_show_hex_num(uint8_t line, uint8_t column, uint32_t num, uint8_t len)
{
	uint8_t i, single_number;
	for (i = 0; i < len; i++)
	{
		single_number = num / oled_pow(16, len - i - 1) % 16;
		if (single_number < 10)
		{
			oled_show_char(line, column + i, single_number + '0');
		}
		else
		{
			oled_show_char(line, column + i, single_number - 10 + 'A');
		}
	}
}


/**
  * @brief  
  * @param  line line start to show, 1~4
  * @param  column column start to show, 1~16
  * @param  number number for show, range 0~1111 1111 1111 1111
  * @param  length length of number string, range1~16
  * @retval 
  */
void oled_show_bin_num(uint8_t line, uint8_t column, uint32_t number, uint8_t length)
{
    uint8_t i;
    for (i = 0; i < length; i++)
    {
        oled_show_char(line, column + i, number / oled_pow(2, length - i - 1) % 2 + '0');
    }
}